;(function(){
    var template = function (filename, content) {
        return typeof content === 'string'
            ? compile(content, {
                filename: filename
            })
            : renderFile(filename, content);
    };
    template.version = '3.0.0';
    template.config = function (name, value) {
        defaults[name] = value;
    };
    //默认配置项
    var defaults = template.defaults = {
        openTag: '<%',    // 逻辑语法开始标签
        closeTag: '%>',   // 逻辑语法结束标签
        escape: true,     // 是否编码输出变量的 HTML 字符
        cache: true,      // 是否开启缓存（依赖 options 的 filename 字段）
        compress: false,  // 是否压缩输出
        parser: null      // 自定义语法格式器 @see: template-syntax.js
    };
    //缓存存储
    var cacheStore = template.cache = {};
    template.render = function (source, options) {
        return compile(source, options);
    };
    var renderFile = template.renderFile = function (filename, data) {
        var fn = template.get(filename) || showDebugInfo({
            filename: filename,
            name: 'Render Error',
            message: 'Template not found'
        });
        return data ? fn(data) : fn;
    };
    template.get = function (filename) {
        var cache;
        if (cacheStore[filename]) {
            // 使用内存缓存
            cache = cacheStore[filename];
        } else if (typeof document === 'object') {
            // 加载模板并编译
            var elem = document.getElementById(filename);
            if (elem) {
                var source = (elem.value || elem.innerHTML).replace(/^\s*|\s*$/g, '');
                cache = compile(source, {
                    filename: filename
                });
            }
        } return cache;
    };
    //
    var toString = function (value, type) {
        if (typeof value !== 'string') {
            type = typeof value;
            if (type === 'number') {
                value += '';
            } else if (type === 'function') {
                value = toString(value.call(value));
            } else {
                value = '';
            }
        } return value;
    };
    //定义转码表
    var escapeMap = {
        "<": "&#60;",
        ">": "&#62;",
        '"': "&#34;",
        "'": "&#39;",
        "&": "&#38;"
    };
    //转码函数
    var escapeFn = function (s) {
        return escapeMap[s];
    };
    //转码html
    var escapeHTML = function (content) {
        return toString(content).replace(/&(?![\w#]+;)|[<>"']/g, escapeFn);
    };
    //数组的isArray方法
    var isArray = Array.isArray || function (obj) {
        return ({}).toString.call(obj) === '[object Array]';
    };
    //each 循环函数
    var each = function (data, callback) {
        var i, len;
        if (isArray(data)) {
            for (i = 0, len = data.length; i < len; i++) {
                callback.call(data, data[i], i, data);
            }
        } else {
            for (i in data) {
                callback.call(data, data[i], i);
            }
        }
    };
    //公共方法
    var utils = template.utils = { $helpers: {}, $include: renderFile, $string: toString, $escape: escapeHTML, $each: each };
    template.helper = function (name, helper) {
        helpers[name] = helper;
    };
    //声明变量 helper
    var helpers = template.helpers = utils.$helpers;
    template.onerror = function (e) {
        var message = 'Template Error\n\n';
        for (var name in e) {
            message += '<' + name + '>\n' + e[name] + '\n\n';
        } if (typeof console === 'object') {
            console.error(message);
        }
    };
    // 模板调试器
    var showDebugInfo = function (e) {
        template.onerror(e);
        return function () {
            return '{Template Error}';
        };
    };
    var compile = template.compile = function (source, options) {// 合并默认配置
        options = options || {};
        for (var name in defaults) {
            if (options[name] === undefined) {
                options[name] = defaults[name];
            }
        }
        var filename = options.filename;
        try {
            var Render = compiler(source, options);
        } catch (e) {
            e.filename = filename || 'anonymous';
            e.name = 'Syntax Error';
            return showDebugInfo(e);
        }
        // 对编译结果进行一次包装
        function render(data) {
            try {
                return new Render(data, filename) + '';
            } catch (e) {
                // 运行时出错后自动开启调试模式重新编译
                if (!options.debug) {
                    options.debug = true;
                    return compile(source, options)(data);
                }
                return showDebugInfo(e)();
            }
        };
        render.prototype = Render.prototype;
        render.toString = function () {
            return Render.toString();
        };
        if (filename && options.cache) {
            cacheStore[filename] = render;
        }
        return render;
    };
    // 数组迭代
    var forEach = utils.$each;
    // 静态分析模板变量
    var KEYWORDS =
        // 关键字
        'break,case,catch,continue,debugger,default,delete,do,else,false'
        + ',finally,for,function,if,in,instanceof,new,null,return,switch,this'
        + ',throw,true,try,typeof,var,void,while,with'    // 保留字
        + ',abstract,boolean,byte,char,class,const,double,enum,export,extends'
        + ',final,float,goto,implements,import,int,interface,long,native'
        + ',package,private,protected,public,short,static,super,synchronized'
        + ',throws,transient,volatile'    // ECMA 5 - use strict
        + ',arguments,let,yield'
        + ',undefined';
    var REMOVE_RE = /\/\*[\w\W]*?\*\/|\/\/[^\n]*\n|\/\/[^\n]*$|"(?:[^"\\]|\\[\w\W])*"|'(?:[^'\\]|\\[\w\W])*'|\s*\.\s*[$\w\.]+/g;
    var SPLIT_RE = /[^\w$]+/g;
    var KEYWORDS_RE = new RegExp(["\\b" + KEYWORDS.replace(/,/g, '\\b|\\b') + "\\b"].join('|'), 'g');
    var NUMBER_RE = /^\d[^,]*|,\d[^,]*/g;
    var BOUNDARY_RE = /^,+|,+$/g;
    var SPLIT2_RE = /^$|,+/;
    // 获取变量
    function getVariable(code) {
        return code
            .replace(REMOVE_RE, '')
            .replace(SPLIT_RE, ',')
            .replace(KEYWORDS_RE, '')
            .replace(NUMBER_RE, '')
            .replace(BOUNDARY_RE, '')
            .split(SPLIT2_RE);
    };
    // 字符串转义
    function stringify(code) {
        return "'" + code
            // 单引号与反斜杠转义
            .replace(/('|\\)/g, '\\$1')
            // 换行符转义(windows + linux)
            .replace(/\r/g, '\\r')
            .replace(/\n/g, '\\n') + "'";
    }
    //编译函数
    function compiler(source, options) {
        var debug = options.debug;
        var openTag = options.openTag;
        var closeTag = options.closeTag;
        var parser = options.parser;
        var compress = options.compress;
        var escape = options.escape;
        var line = 1;
        var uniq = { $data: 1, $filename: 1, $utils: 1, $helpers: 1, $out: 1, $line: 1 };
        var isNewEngine = ''.trim;// '__proto__' in {}
        var replaces = isNewEngine
            ? ["$out='';", "$out+=", ";", "$out"]
            : ["$out=[];", "$out.push(", ");", "$out.join('')"];

        var concat = isNewEngine
            ? "$out+=text;return $out;"
            : "$out.push(text);";

        var print = "function(){"
            + "var text=''.concat.apply('',arguments);"
            + concat
            + "}";
        var include = "function(filename,data){"
            + "data=data||$data;"
            + "var text=$utils.$include(filename,data,$filename);"
            + concat
            + "}";
        var headerCode = "'use strict';"
            + "var $utils=this,$helpers=$utils.$helpers,"
            + (debug ? "$line=0," : "");
        var mainCode = replaces[0];
        var footerCode = "return new String(" + replaces[3] + ");"// html与逻辑语法分离
        forEach(source.split(openTag), function (code) {
            code = code.split(closeTag);
            var $0 = code[0];
            var $1 = code[1];
            // code: [html]
            if (code.length === 1) {
                mainCode += html($0);
            } else {
                mainCode += logic($0);
                if ($1) {
                    mainCode += html($1);
                }
            }
        });
        var code = headerCode + mainCode + footerCode;// 调试语句
        if (debug) {
            code = "try{" + code + "}catch(e){"
                + "throw {"
                + "filename:$filename,"
                + "name:'Render Error',"
                + "message:e.message,"
                + "line:$line,"
                + "source:" + stringify(source)
                + ".split(/\\n/)[$line-1].replace(/^\\s+/,'')"
                + "};"
                + "}";
        } try {
            var Render = new Function("$data", "$filename", code);
            Render.prototype = utils;
            return Render;
        } catch (e) {
            e.temp = "function anonymous($data,$filename) {" + code + "}";
            throw e;
        }
        // 处理 HTML 语句
        function html(code) {
            // 记录行号
            line += code.split(/\n/).length - 1;        // 压缩多余空白与注释
            if (compress) {
                code = code
                    .replace(/\s+/g, ' ')
                    .replace(/<!--[\w\W]*?-->/g, '');
            }
            if (code) {
                code = replaces[1] + stringify(code) + replaces[2] + "\n";
            }
            return code;
        }
        // 处理逻辑语句
        function logic(code) {
            var thisLine = line;
            if (parser) {
                // 语法转换插件钩子
                code = parser(code, options);
            } else if (debug) {
                // 记录行号
                code = code.replace(/\n/g, function () {
                    line++;
                    return "$line=" + line + ";";
                });
            }
            // 输出语句. 编码: <%=value%> 不编码:<%=#value%>
            // <%=#value%> 等同 v2.0.3 之前的 <%==value%>
            if (code.indexOf('=') === 0) {
                var escapeSyntax = escape && !/^=[=#]/.test(code);
                code = code.replace(/^=[=#]?|[\s;]*$/g, '');            // 对内容编码
                if (escapeSyntax) {
                    var name = code.replace(/\s*\([^\)]+\)/, '');// 排除 utils.* | include | print
                    if (!utils[name] && !/^(include|print)$/.test(name)) {
                        code = "$escape(" + code + ")";
                    }            // 不编码
                } else {
                    code = "$string(" + code + ")";
                }
                code = replaces[1] + code + replaces[2];
            }
            if (debug) {
                code = "$line=" + thisLine + ";" + code;
            }
            // 提取模板中的变量名
            forEach(getVariable(code), function (name) {
                // name 值可能为空，在安卓低版本浏览器下
                if (!name || uniq[name]) {
                    return;
                }
                var value;            // 声明模板变量
                // 赋值优先级:
                // [include, print] > utils > helpers > data
                if (name === 'print') {
                    value = print;
                } else if (name === 'include') {
                    value = include;
                } else if (utils[name]) {
                    value = "$utils." + name;
                } else if (helpers[name]) {
                    value = "$helpers." + name;
                } else {
                    value = "$data." + name;
                }
                headerCode += name + "=" + value + ",";
                uniq[name] = true;
            });
            return code + "\n";
        }
    };
    // 定义模板引擎的语法
    defaults.openTag = '{{';
    defaults.closeTag = '}}';
    var filtered = function (js, filter) {
        var parts = filter.split(':');
        var name = parts.shift();
        var args = parts.join(':') || '';
        if (args) {
            args = ', ' + args;
        }
        return '$helpers.' + name + '(' + js + args + ')';
    }
    defaults.parser = function (code, options) {
        // var match = code.match(/([\w\$]*)(\b.*)/);
        // var key = match[1];
        // var args = match[2];
        // var split = args.split(' ');
        // split.shift();
        code = code.replace(/^\s/, '');
        var split = code.split(' ');
        var key = split.shift();
        var args = split.join(' ');
        switch (key) {
            case 'if':
                code = 'if(' + args + '){';
                break;
            case 'else':
                if (split.shift() === 'if') {
                    split = ' if(' + split.join(' ') + ')';
                } else {
                    split = '';
                }
                code = '}else' + split + '{';
                break;
            case '/if':
                code = '}';
                break;
            case 'each':
                var object = split[0] || '$data';
                var as = split[1] || 'as';
                var value = split[2] || '$value';
                var index = split[3] || '$index';
                var param = value + ',' + index;
                if (as !== 'as') {
                    object = '[]';
                }
                code = '$each(' + object + ',function(' + param + '){';
                break;
            case '/each':
                code = '});';
                break;
            case 'echo':
                code = 'print(' + args + ');';
                break;
            case 'print':
            case 'include':
                code = key + '(' + split.join(',') + ');';
                break;
            default:
                // 过滤器（辅助方法）
                // {{value | filterA:'abcd' | filterB}}
                // >>> $helpers.filterB($helpers.filterA(value, 'abcd'))
                // TODO: {{ddd||aaa}} 不包含空格
                if (/^\s*\|\s*[\w\$]/.test(args)) {
                    var escape = true;                // {{#value | link}}
                    if (code.indexOf('#') === 0) {
                        code = code.substr(1);
                        escape = false;
                    } var i = 0;
                    var array = code.split('|');
                    var len = array.length;
                    var val = array[i++]; for (; i < len; i++) {
                        val = filtered(val, array[i]);
                    }
                    code = (escape ? '=' : '=#') + val;            // 即将弃用 {{helperName value}}
                } else if (template.helpers[key]) {
                    code = '=#' + key + '(' + split.join(',') + ');';
                    // 内容直接输出 {{value}}
                } else {
                    code = '=' + code;
                }
                break;
        }
        return code;
    };
    this.template = template
}());
//模板扩展



